/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou Yi Dev, Ltd. All Right Reserved.
 * @address:     https://yiyuery.github.io/
 * @date:        2019/5/20 20:57
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用，转发请注明出处.
 */
package com.example.component.analyzer.service.gold.impl;

import com.example.common.api.exception.BusinessException;
import com.example.common.api.exception.code.ApiErrorCode;
import com.example.common.api.service.jpa.BaseJpaServiceImpl;
import com.example.common.api.service.jpa.BaseRepository;
import com.example.common.util.CapDateTimeUtil;
import com.example.common.util.CapMathUtil;
import com.example.component.analyzer.common.dto.GoldChartDTO;
import com.example.component.analyzer.common.enums.GoldChartDataTypeEnum;
import com.example.component.analyzer.core.dao.GoldRepository;
import com.example.component.analyzer.core.model.Gold;
import com.example.component.analyzer.service.gold.GoldDataAnalysisService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 *
 * </p>
 *
 * @author Yi
 * @version v1.0.0
 * @date 2019-07-03 22:26
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019-07-03
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Service
@Slf4j
public class GoldDataAnalysisServiceImpl extends BaseJpaServiceImpl<Gold, Long> implements GoldDataAnalysisService {

    @Resource
    private GoldRepository goldRepository;

    /**
     * 获取Dao
     *
     * @return
     */
    @Override
    protected BaseRepository getRepository() {
        return goldRepository;
    }

    /**
     * 根据数据类型加载数据
     *
     * @param type
     * @return
     */
    @Override
    public GoldChartDTO loadChartDataWithType(Integer type) {
        GoldChartDTO result = new GoldChartDTO();
        List<Gold> goldList = null;
        List<String> scales = Lists.newArrayList();
        List<String> labels = Lists.newArrayList();
        switch (GoldChartDataTypeEnum.codeOf(type)) {
            case GOLD_CHART_DATA_TYPE_DAY:
                scales.addAll(CapDateTimeUtil.getInstance().getHourOfDayScale());
                labels.addAll(CapDateTimeUtil.getInstance().getHourLabelsOfDayScale());
                goldList = goldRepository.findByCreateTimeBetween(
                        CapDateTimeUtil.getInstance().get24HourStart().getDate(),
                        CapDateTimeUtil.currentDate()
                        );
                break;
            case GOLD_CHART_DATA_TYPE_WEEK:
                scales.addAll(CapDateTimeUtil.getInstance().getDateOfWeekScale());
                labels.addAll(CapDateTimeUtil.getInstance().getDayLabelsOfWeekScale());
                goldList = goldRepository.findByCreateTimeBetween(
                        CapDateTimeUtil.getInstance().getLastWeekTodayEnd().getDate(),
                        CapDateTimeUtil.currentDate());
                break;
            case GOLD_CHART_DATA_TYPE_MONTH:
                scales.addAll(CapDateTimeUtil.getInstance().getDayOfMonthScale(CapDateTimeUtil.DateFormatConstant.MMDD));
                labels.addAll(CapDateTimeUtil.getInstance().getDayOfMonthScale(CapDateTimeUtil.DateFormatConstant.MM_DD));
                goldList = goldRepository.findByCreateTimeBetween(
                        CapDateTimeUtil.getInstance().getLastMonthTodayEnd().getDate(),
                        CapDateTimeUtil.currentDate());
                break;
            case GOLD_CHART_DATA_TYPE_YEAR:
                scales.addAll(CapDateTimeUtil.getInstance().getMonthOfYearScale(CapDateTimeUtil.DateFormatConstant.YYMM));
                labels.addAll(CapDateTimeUtil.getInstance().getMonthOfYearScale(CapDateTimeUtil.DateFormatConstant.YY_MM));
                goldList = goldRepository.findByCreateTimeBetween(
                        CapDateTimeUtil.getInstance().getLastYearTodayEnd().getDate(),
                        CapDateTimeUtil.currentDate());
                break;
            default:
                break;
        }
        transformData(result, scales, goldList, type);
        result.setScales(labels);
        return result;
    }

    /**
     * 数据转换
     *
     * @param result
     * @param scales
     * @param goldList
     * @param type
     * @return
     */
    private void transformData(GoldChartDTO result, List<String> scales, List<Gold> goldList, Integer type) {
        String[] valueProperties = new String[]{"buyRate","sellRate","referPrice"};
        String dateFormat = "";
        switch (GoldChartDataTypeEnum.codeOf(type)) {
            //一天内(24小时内)
            case GOLD_CHART_DATA_TYPE_DAY:
                dateFormat = CapDateTimeUtil.DateFormatConstant.MMDDHH;
                break;
            //一周内（7天内）&& 一月内(30天)
            case GOLD_CHART_DATA_TYPE_WEEK:
            case GOLD_CHART_DATA_TYPE_MONTH:
                dateFormat = CapDateTimeUtil.DateFormatConstant.MMDD;
                break;
            //一年内（12月）
            case GOLD_CHART_DATA_TYPE_YEAR:
                dateFormat = CapDateTimeUtil.DateFormatConstant.YYMM;
                break;
            default:
                break;
        }
        result.setBuyRates(calculateWithPropertyKey(valueProperties[0],goldList,scales,dateFormat));
        result.setSellRates(calculateWithPropertyKey(valueProperties[1],goldList,scales,dateFormat));
        result.setReferPrices(calculateWithPropertyKey(valueProperties[2],goldList,scales,dateFormat));
        result.setMax(CapMathUtil.maxOfDouble(result.getBuyRates()));
        result.setMin(CapMathUtil.minOfDouble(result.getBuyRates()));
    }

    /**
     * 根据变量名动态解析当天数据
     * - CapDateTimeUtil.DateFormatConstant.HH
     * - CapDateTimeUtil.DateFormatConstant.MMDD
     * @param valueProperty
     * @param goldList
     * @param scales
     * @return
     */
    private List<Double> calculateWithPropertyKey(String valueProperty, List<Gold> goldList, List<String> scales,String fmt) {
        Map<String, List<Double>> statMap = Maps.newHashMap();
        Field field = ReflectionUtils.findRequiredField(Gold.class, valueProperty);
        goldList.forEach(p -> {
            if (Objects.nonNull(p.getCreateTime())) {
                String key = CapDateTimeUtil.getInstance().dateFormat(p.getCreateTime().getTime(),fmt);
                if (!statMap.containsKey(key)) {
                    statMap.put(key, Lists.newArrayList());
                }
                try {
                    field.setAccessible(true);
                    statMap.get(key).add(Double.valueOf((String)field.get(p)));
                } catch (Exception e) {
                    log.error("calculateWithPropertyKey failed!",e);
                    throw new BusinessException(ApiErrorCode.SYSTEM_ERROR.getCode());
                }
            }
        });
        List<Double> yData = Lists.newArrayList();
        for (String xItem : scales) {
            if(statMap.containsKey(xItem)){
                yData.add(CapMathUtil.averageOfDouble(statMap.get(xItem)));
            }else{
                yData.add(0D);
            }
        }
        return yData;
    }
}
